﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommandManager.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Windows.Input.Commands
{
    using System;
    using System.Collections.Generic;
    using Threading;

    public class CommandManager
    {
        #region Fields

        private static ICommandRouter _commandRouter;
        private static List<WeakReference> _requerySuggestedHandlers;
        private const int MaxDispatchCycles = 64;

        #endregion

#if !SILVERLIGHT
        private static readonly EventHandler RequerySuggestedHandler = (s, e) => CommandManager.InvalidateRequerySuggested();

        static CommandManager()
        {
            
            System.Windows.Input.CommandManager.RequerySuggested += RequerySuggestedHandler;
        }
#endif

        protected enum DispatchState
        {
            None, 
            Pending, 
            Running
        }

        public static ICommandRouter CommandRouter
        {
            get
            {
                return _commandRouter ?? (_commandRouter = new CommandRouter());
            }
            set
            {
                if (_commandRouter != value)
                {
                    _commandRouter = value;
                    InvalidateRequerySuggested();
                }
            }
        }

        public static void InvalidateRequerySuggested()
        {
            if (DispatcherState == DispatchState.Running)
            {
                PendingUpdates++;
            }
            else if (DispatcherState != DispatchState.Pending && UIDispatcher.Current != null)
            {
                DispatcherState = DispatchState.Pending;
                UIDispatcher.Current.BeginInvoke(DispatchEvents);
            }
        }

        private static DispatchState DispatcherState { get; set; }

        protected static int PendingUpdates { get; set; }

        private static void DispatchEvents()
        {
            DispatcherState = DispatchState.Running;
            RaiseWeakEvents(_requerySuggestedHandlers);
            DispatcherState = DispatchState.None;
            if (PendingUpdates != 0)
            {
                PendingUpdates = 0;
                if (++DispatchCycles > MaxDispatchCycles)
                {
                    throw new InvalidOperationException("Cyclic command refresh detected.");
                }

                // try again
                InvalidateRequerySuggested();
            }
            else
            {
                DispatchCycles = 0;
            }
        }

        protected static int DispatchCycles { get; set; }

        protected static bool IsDispatchPending { get; set; }

        protected static bool IsDispatching { get; set; }

        internal static ICommandRouter GetCommandRouter(RoutedCommand command)
        {
            return CommandRouter;
        }

        public static event EventHandler RequerySuggested
        {
            add
            {
                if (_requerySuggestedHandlers == null)
                {
                    _requerySuggestedHandlers = new List<WeakReference>();
                }

                AddWeakEventHandler(_requerySuggestedHandlers, value);
            }
            remove
            {
                RemoveWeakEventHandler(_requerySuggestedHandlers, value);
            }
        }

        private static void RemoveWeakEventHandler(IList<WeakReference> handlers, EventHandler handler)
        {
            if (handlers == null)
            {
                return;
            }

            for (var i = handlers.Count - 1; i >= 0; i--)
            {
                var target = handlers[i].Target as EventHandler;
                if (target == null || target == handler)
                {
                    handlers.RemoveAt(i);
                }
            }
        }

        private static void AddWeakEventHandler(ICollection<WeakReference> handlers, EventHandler handler)
        {
            handlers.Add(new WeakReference(handler));
        }

        private static void RaiseWeakEvents(IList<WeakReference> handlers)
        {
            if (handlers != null)
            {
                var handlerArray = new EventHandler[handlers.Count];
                var index = 0;
                for (var i = handlers.Count - 1; i >= 0; i--)
                {
                    var target = handlers[i].Target as EventHandler;
                    if (target == null)
                    {
                        handlers.RemoveAt(i);
                    }
                    else
                    {
                        handlerArray[index++] = target;
                    }
                }

                for (var j = 0; j < index; j++)
                {
                    handlerArray[j](null, EventArgs.Empty);
                }
            }
        }
    }
}